Android namespace权限问题

Android namespace权限问题

在做OpenXR Runtime的过程中,打算把Runtime apk直接预置到/system/app分区下,主要是因为Runtime Apk本身算的上是一个“系统应用”,直接预置是一个比较方便的做法。

Runtime的机制

OpenXR Runtime的机制,实际是一个dlopen的过程,根据之前的经验和认知,整个OpenXR App实际是一个链式调用的过程。

image-20220806135004628

因此,Application实际是通过集成的libopenxr_loader.so去发现系统中的libopenxr_runtime.so,然后通过dlopen的方式把Runtime加载到Application进程中。

  • 由于我们把OpenXR Runtime集成到了/system/app分区,所以肯定会把其中的libopenr_runtime.so预置到/system/app/OpenXRRuntime/lib/arm64/libopenxr_monado.so

  • Broker在做发现runtime so动作的时候,就会告知Application去加载/system/app/OpenXRRuntime/lib/arm64/libopenxr_monado.so

  • 这个时候就会触发类似下文的错误,提示无法dlopen

    E/linker: library “/system/app/OpenXRRuntime/lib/arm64/libopenxr_monado.so” (/system/app/OpenXRRuntime/lib/arm64/libopenxr_monado.so”) needed or dlopened by “xxxxxxx is not accessible for the namespace: [name=”classloader-namespace”, ld_library_paths=””, default_library_paths=””, permitted_paths=”xxxxxxx”]

问题的原因

正如错误日志所表述的,发生问题的原因其实是因为Application中的libopenxr_loader.so是没有权限去加载/system/app/OpenXRRuntime/lib/arm64/libopenxr_monado.so

这个部分的内容早在Android 7.0的版本上就已经引入了:原生库的命名空间

image-20220806140125884

AOSP的官方文档也说的很清楚,namespace的出现是防止/避免应用意外使用平台的库,可是我们这边就是应用需要使用平台的库,针对这种情况,AOSP的文档也非常友好的给出了几个建议。

解决的方法

我们先看一下AOSP文档中的几个建议,最后再来说明实际的解决方案。

方案一:添加库到声明的txt文件里

这个是AOSP文档中给出的第一条建议,也是非常正规的一套做法,即在特定的声明txt文件中明示可以被应用加载库。其中txt的文件会有两处:

  • /vendor/etc/public.libraries.txt(对于芯片供应商的库)
  • /system/etc/public.libraries-COMPANYNAME.txt(对于设备制造商的库)

格式上也做比较好的隔离,可以区分是芯片供应商添加的,还是具体设备制造商添加的,此外,针对命名还有这一套强规范限定:

image-20220806141211385

不得不说,Android在应对碎片化的过程中还是做了很多努力的,隔离的部分已经是做的很好了,但是我在实际操作过程里添加后还是NG,这里就出现了第二个坑,它的限定是针对/system/lib/以及/system/lib64而言的,我们的目录是:/system/app/OpenXRRuntime/lib/arm64/libopenxr_monado.so,所以这套方案就不行了。

方案二:把runtime so放到/system/lib目录下

这个方案是从方案一的失败案例上衍生出来,自然而然可以想到的,但是在实际操作中发现不是优选方案:

  • libopenxr_monado.so是依赖Monado整体编译的,根据《OpenXR223-OutOfProcess实现框架》一文的介绍,这部分主要是帮助实现Client-Server的IPC通讯连接,把client端的OpenXR Command发送到Server端。
    • 所以libopenxr_monado.so是存在变动可能的
  • 通过public公开的so需要具备32/64bit两套,而现在的Runtime本身是以64bit运行的,所以,还需要去额外凑一个32bit的库放到/system/lib目录下,不然就是开机NG无限重启。

当然,在实际操作过程中,即使我们把libopenxr_monado.so放到了/system/lib/system/lib64两个目录下去,虽然不再出现libopenxr_monado.sodenied,但会报出libc++_shared.sonot found error,究其原因是因为libopenxr_monado.so在编译的时候会link libc++_shared.so,而如果我们把这个库放进去的话,显然影响面就太大了,因为每个NDK版本的libc++_shared.so可能都不太一样,那么应用可能就会出现各种莫名的crash了。

方案三:究其所以然

这个也是最终的方案,实际上是有一点patch的味道,具体的修改方案可以参考:《第三方app加载系统/system/lib下的库–is not accessible for the namespace》,就是去源码中找一下是哪里阻止了load so,然后针对性的修改一下。CSDN中博客的部分是针对Android 7的平台,实际在Android 12的版本上:

https://android.googlesource.com/platform/bionic/+/refs/tags/android-12.1.0_r19/linker/linker.cpp#191

img

可以在红框的部分加上libopenxr_monado.so,然后在使用名单的地方做一下兼容处理:

img

这样的话,实际就是在linkerlibrary检查的时候让它认为libopenxr_monado.so也是一个特例库,从而Application可以用到它。

参考资料

  1. 《android Linker:namespace隔离机制》:https://www.jianshu.com/p/1672b52548ce
  2. 《第三方app加载系统/system/lib下的库–is not accessible for the namespace》:https://blog.csdn.net/u012459903/article/details/100731487
  3. 《原生库的命名空间》:https://source.android.com/devices/tech/config/namespaces_libraries?hl=zh-cn
  4. 《链接器命名空间》:https://source.android.com/devices/architecture/vndk/linker-namespace?hl=zh-cn